package com.dc.common.utils;

import com.dc.common.core.text.CharsetKit;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author zhuangcy
 * @date 2020/10/20 14:36
 * @description RSA加密对明文的长度有所限制，规定需加密的明文最大长度=密钥长度-11（单位是字节，即byte）
 */
public class RsaUtil {

    /**
     * 公钥
     */
    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQ0HdvpDAeU4cLKGVxiv24742obgyJZTuAkMUiYpB5lJk7jb7613Nk+lDGOpBySfKgva2mxBAN3vK7q81AqajtUVgUAy9IuEI8rMh+hlLv86+4ZfpuhG3S2lziyUGyIhc6rpPa+4cLh82DhCcGL+WSY78EVMEtD+pJXRbRkUmIdwIDAQAB";

    /**
     * 私钥
     */
    public static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJDQd2+kMB5ThwsoZXGK/bjvjahuDIllO4CQxSJikHmUmTuNvvrXc2T6UMY6kHJJ8qC9rabEEA3e8rurzUCpqO1RWBQDL0i4QjysyH6GUu/zr7hl+m6EbdLaXOLJQbIiFzquk9r7hwuHzYOEJwYv5ZJjvwRUwS0P6kldFtGRSYh3AgMBAAECgYAw5KGeqPGyG4N5ye7OMh5OBVtvpZBIB0zasIZOgLbExd4ilE4JxRpp2nXRJ87DN6JakRADQSkmdcLAiFXpYh7tU2SQD3AH3VMDqWtrxtHW0lasRm3SyNK3RydP3ImXH+ReefCt+Lsiij9iC2kmfGfH7krDY2Qz5t2cGQJkJplI8QJBAPXANn0J4UpaSJbow4/9KnDR0jTrOl96A5TpYlIwLdlgD/4QYJDIi+D3A2pBMp0VokPcyoQ8kHm+5f+4OTF9ZjkCQQCW2pfo7RRvyCAvA4BNPRMbNZD7BEqGJIE8a4qxwXKWrVn7e43WaMy2VJgEkqF+clv/mLdpe+5cDLHJp7OWAeQvAkEA36RZsfbg0KJS9wrClfHlwqAf9UqwZ50JT2YItRTlVLIoO6S5zGjFayhAMl0861l5vtpabncTX8gLVWlYmyIseQJALXl/yauOOZQuf32is6XxmIYxSjok7QMcIkaY7LTQAjQHqBO60iONcnlVyisZm7CFoys6PwYo+H6rKYrwTAwN6QJBAMzkz3LeR9MZn3urgsWnjVrdQ3+GuZUgYVeh53LJNLNZm1/+fE05OeldK9evSraJEDKeN8fYwYqvg1gq+IGW8Z4=";

    /**
     * RSA默认加密前的明文最大长度117字节
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * 解密密文最大长度为128字
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(1024, new SecureRandom());
        return generator.generateKeyPair();
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.getDecoder().decode(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.getDecoder().decode((publicKey.getBytes()));
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data 待加密数据
     * @return
     * @throws Exception
     */
    public static String encrypt(String data) {
        try {
            return encrypt(data, getPublicKey(PUBLIC_KEY));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("RSA加密");
    }

    /**
     * RSA加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return new String(Base64.getEncoder().encode(encryptedData));
    }

    /**
     * RSA解密
     *
     * @param data 待解密数据
     * @return
     */
    public static String decrypt(String data) {
        try {
            return decrypt(data, getPrivateKey(PRIVATE_KEY));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("RSA解密");
    }


    /**
     * RSA解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.getDecoder().decode((data));
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的内容
        return new String(decryptedData, CharsetKit.UTF_8);
    }

    /**
     * 签名
     *
     * @param data 待签名数据
     * @return 签名
     */
    public static String sign(String data) {
        try {
            return sign(data, getPrivateKey(PRIVATE_KEY));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("生成签名失败");
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.getEncoder().encode(signature.sign()));
    }

    /**
     * 验签
     *
     * @param original 原始字符串
     * @param data     加密字符串
     * @return 是否验签通过
     */
    public static boolean verifySign(String original, String data) {
        try {
            return verifySign(original, getPublicKey(PUBLIC_KEY), data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("验证签名失败");
    }

    /**
     * 验签
     *
     * @param original  原始字符串
     * @param publicKey 公钥
     * @param sign      签名
     * @return 是否验签通过
     */
    public static boolean verifySign(String original, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(original.getBytes());
        return signature.verify(Base64.getDecoder().decode((sign.getBytes())));
    }

}